import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;

public class HandshakerR {
		
		//ATTRIBUTI DELLA CLASSE
		InetAddress IPChannel;
		int portChannel;
		InetAddress IPReceiver;
		int portReceiver;
		int portACK;
		double y;
		int H;
		DatagramSocket receiverSocket;
		DatagramSocket ACKsocket;
		
		InetAddress IPDestination;
		int portDestination;
		int N;
		double x;
		int Lr;
		Packet firstMessage;
		
		//METODO COSTRUTTORE
		public HandshakerR (InetAddress IPChannel, int portChannel, InetAddress IPReceiver, int portReceiver, int portACK, double y, int H, DatagramSocket receiverSocket, DatagramSocket ACKsocket){
			this.IPChannel = IPChannel;
			this.portChannel = portChannel;
			this.IPReceiver = IPReceiver;
			this.portReceiver = portReceiver;
			this.portACK = portACK;
			this.y = y;
			this.H = H;
			this.receiverSocket = receiverSocket;
			this.ACKsocket = ACKsocket;
		}
		
		//METODI DELLA CLASSE
		public void start() throws IOException, InterruptedException{
			
			byte[] UDPdata = new byte[10000];  
			DatagramPacket UDPpacketR = new DatagramPacket(UDPdata, UDPdata.length);
			
			Packet syn = null;
			boolean interrupt = false;
			while (!interrupt) {
				receiverSocket.receive(UDPpacketR);
				syn = new Packet(UDPpacketR.getData());
				if (syn.getIDPacket() == 65530){
					interrupt = true;
					System.out.println("Ricevuto: syn");
				}
			}	
			this.x = (double)syn.getData()[0]/1000;   
			this.IPDestination = syn.getIPSource();
			this.portDestination = syn.getPortSource();
			
			this.Lr = (int)Math.round(this.dicotomic(0.4, x, y, H));
			
			byte[] message = new byte[Lr - H];
			message[0] = (byte)Math.round(this.y*1000);  
			
			Packet syn1 = new Packet(IPDestination, portDestination, IPReceiver, portReceiver, 65531, message);
			DatagramPacket UDPpacketS = new DatagramPacket(syn1.toByte(), syn1.toByte().length, IPChannel, portChannel);
			
			UDPdata = new byte[10000];
			UDPpacketR = new DatagramPacket(UDPdata, UDPdata.length);
			
			ParallelSending ps = new ParallelSending(this.ACKsocket, UDPpacketS);
			Thread t = new Thread(ps);
			t.start();
			Packet syn2 = null;
			while (!ps.getResponse()){
				this.receiverSocket.receive(UDPpacketR);
				syn2 = new Packet(UDPpacketR.getData());
				if (syn2.getIDPacket() == 65532) {
					ps.setResponse();
					System.out.println("Ricevuto: syn2");
				}
			}
			
			
			this.N = ((int)syn2.getData()[1]+128)*256 + (int)syn2.getData()[2]+128;
			
			message = new byte[Lr - H];
			
			Packet syn3 = new Packet(IPDestination, portDestination, IPReceiver, portReceiver, 65533, message);
			UDPpacketS = new DatagramPacket(syn3.toByte(), syn3.toByte().length, IPChannel, portChannel);
			
			UDPdata = new byte[10000];
			UDPpacketR = new DatagramPacket(UDPdata, UDPdata.length);
			
			ps = new ParallelSending(this.ACKsocket, UDPpacketS);
			t = new Thread(ps);
			t.start();
			Packet data0 = null;
			while (!ps.getResponse()){
				this.receiverSocket.receive(UDPpacketR);
				data0 = new Packet(UDPpacketR.getData());
				if (data0.getIDPacket() < 65530) {
					ps.setResponse();
					System.out.println("Ricevuto: data0");
				}
			}
			
			this.firstMessage = data0;
		}
		
		public int getN() {
			return this.N;
		}
		
		public int getLr() {
			return Lr;
		}
		
		public double getx() {
			return x;
		}
		
		public Packet getFirstMessage() {
			return this.firstMessage;
		}
		
		public InetAddress getIPDestinatio() {
			return this.IPDestination;
		}
		
		public int getPortDestination(){
			return this.portDestination;
		}
		
		
		//METODI PRIVATI
		private double dicotomic (double tol, double x, double y, int H) {
			
			double inf = 100;
			double sup = 9000;
			boolean trovato = false;

			double d_inf = y*Math.exp(inf/1024)*(inf-(H+1024))*inf*Math.pow(Math.log(inf),2)-1048576*(inf*(1+Math.log(inf))-H)-x*1024*inf*Math.pow(Math.log(inf),2);
			double d_sup = y*Math.exp(sup/1024)*(sup-(H+1024))*sup*Math.pow(Math.log(sup),2)-1048576*(sup*(1+Math.log(sup))-H)-x*1024*sup*Math.pow(Math.log(sup),2);
			
			if (d_inf*d_sup > 0)
				return -1;//////////////////
			
			double m = (sup + inf)/2;
			double d_m = y*Math.exp(m/1024)*(m-(H+1024))*m*Math.pow(Math.log(m),2)-1048576*(m*(1+Math.log(m))-H)-x*1024*m*Math.pow(Math.log(m),2);
			
			
			if (d_m == 0)
				trovato = true;
			
			while (!trovato && ((sup - inf) > tol)) {
				
				if (d_inf*d_m > 0) {
					inf = m;
					d_inf = y*Math.exp(inf/1024)*(inf-(H+1024))*inf*Math.pow(Math.log(inf),2)-1048576*(inf*(1+Math.log(inf))-H)-x*1024*inf*Math.pow(Math.log(inf),2);
				} else {
					sup = m;
					d_sup = y*Math.exp(sup/1024)*(sup-(H+1024))*sup*Math.pow(Math.log(sup),2)-1048576*(sup*(1+Math.log(sup))-H)-x*1024*sup*Math.pow(Math.log(sup),2);
				}
				
				m = (sup + inf)/2;
				d_m = y*Math.exp(m/1024)*(m-(H+1024))*m*Math.pow(Math.log(m),2)-1048576*(m*(1+Math.log(m))-H)-x*1024*m*Math.pow(Math.log(m),2);
				
				if (d_m == 0)
					trovato = true;
				
			}
			return m;
		}
		
		//CLASSI INTERNE
		private class ParallelSending implements Runnable {
			
			//ATTRIBUTI DI CLASSE
			private DatagramPacket UDPpacket;
			private DatagramSocket socket;
			private boolean response;
			// METODO COSTRUTTORE
			public ParallelSending(DatagramSocket socket, DatagramPacket UDPpacket){
				this.socket = socket;
				this.UDPpacket = UDPpacket;
				this.response = false;
			}
			//METODI DI CLASSE
			public void run(){
				System.out.println("Invio...");
				while(!response)
					try {
						socket.send(UDPpacket);
					} catch (IOException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
			}
			public void setResponse(){
				this.response = true;
			}
			public boolean getResponse(){
				return this.response;
			}
		}
			
}

